/*
 * tetris.c
 *
 *  Created on: 18.09.2011
 *      Author: Erik Wagner
 */

/*
 * Struktur für ein Punkt
 */
#include "stdlib.h"
#include "atmelLib.h"
#include "tetris.h"
#include "stdio.h"
#include "conio.h"

#define ROWS 16

static unsigned char statePoints[ROWS];

/*
 * Definitionen der Bausteine für ein Koordinatensytem das oben-links beginnt
 */

tetrisFigure createI() {
	tetrisFigure re;
	re.center.row = 0;
	re.center.column = 4;
	re.p1.row = 0;
	re.p1.column = 2;
	re.p2.row = 0;
	re.p2.column = 3;
	re.p3.row = 0;
	re.p3.column = 4;
	re.p4.row = 0;
	re.p4.column = 5;
	return re;
}

tetrisFigure createJ() {
	tetrisFigure re;
	re.center.row = 0;
	re.center.column = 4;
	re.p1.row = 0;
	re.p1.column = 2;
	re.p2.row = 0;
	re.p2.column = 3;
	re.p3.row = 0;
	re.p3.column = 4;
	re.p4.row = 1;
	re.p4.column = 4;
	return re;
}

tetrisFigure createL() {
	tetrisFigure re;
	re.center.row = 1;
	re.center.column = 4;
	re.p1.row = 1;
	re.p1.column = 2;
	re.p2.row = 1;
	re.p2.column = 3;
	re.p3.row = 1;
	re.p3.column = 4;
	re.p4.row = 0;
	re.p4.column = 4;
	return re;
}

tetrisFigure createO() {
	tetrisFigure re;
	re.center.row = 1;
	re.center.column = 4;
	re.p1.row = 0;
	re.p1.column = 3;
	re.p2.row = 0;
	re.p2.column = 4;
	re.p3.row = 1;
	re.p3.column = 3;
	re.p4.row = 1;
	re.p4.column = 4;
	return re;
}

tetrisFigure createS() {
	tetrisFigure re;
	re.center.row = 1;
	re.center.column = 4;
	re.p1.row = 0;
	re.p1.column = 4;
	re.p2.row = 0;
	re.p2.column = 5;
	re.p3.row = 1;
	re.p3.column = 4;
	re.p4.row = 1;
	re.p4.column = 3;
	return re;
}

tetrisFigure createT() {
	tetrisFigure re;
	re.center.row = 1;
	re.center.column = 4;
	re.p1.row = 0;
	re.p1.column = 4;
	re.p2.row = 1;
	re.p2.column = 3;
	re.p3.row = 1;
	re.p3.column = 4;
	re.p4.row = 1;
	re.p4.column = 5;
	return re;
}

tetrisFigure createZ() {
	tetrisFigure re;
	re.center.row = 1;
	re.center.column = 4;
	re.p1.row = 0;
	re.p1.column = 3;
	re.p2.row = 0;
	re.p2.column = 4;
	re.p3.row = 1;
	re.p3.column = 4;
	re.p4.row = 1;
	re.p4.column = 5;
	return re;
}

/*
 * Erzeugt eine zufällige Figur
 */
tetrisFigure createRandomFigure() {
	int randomNum = rand() % 7;
	if (randomNum == 0) {
		return createI();
	} else {
		if (randomNum == 1) {
			return createJ();
		} else {
			if (randomNum == 2) {
				return createL();
			} else {
				if (randomNum == 3) {
					return createO();
				} else {
					if (randomNum == 4) {
						return createS();
					} else {
						if (randomNum == 5) {
							return createT();
						} else {
							return createZ();
						}
					}
				}
			}
		}
	}
}

/*
 * Gibt zurück, ob der Punkt belegt ist oder nicht
 *
 * 0 --> nicht belegt
 * anderer Wert --> belegt
 */
char isPointSet(char row, char column) {
	if (row > ROWS - 1 || column > 7 || row < 0 || column < 0) {
		return 1;
	} else {
		return (statePoints[row] & (twoPot(column)));
	}
}

char isPointSet(point p) {
	return isPointSet(p.row, p.column);
}

/*
 * Rotiert einen Punkt p um einen Punkt center um 90° im Uhrzeigersinn
 */
point rotatePoint(point p, point center) {
	point re;
	re.row = center.row + (p.column - center.column);
	re.column = center.column - (p.row - center.row);
	return re;
}

/*
 * Rotiert eine Figur um 90° im Uhrzeigersinn
 */
tetrisFigure rotate(tetrisFigure fig) {
	tetrisFigure re;
	re.center = fig.center;
	re.p1 = rotatePoint(fig.p1, fig.center);
	re.p2 = rotatePoint(fig.p2, fig.center);
	re.p3 = rotatePoint(fig.p3, fig.center);
	re.p4 = rotatePoint(fig.p4, fig.center);
	return re;
}

/*
 * Bewegt den Tetrisstein um 1 Feld nach unten
 * Es sollte vorher getestet werden, ob die Bewegung möglich ist
 */
tetrisFigure moveDown(tetrisFigure fig) {
	tetrisFigure re;
	re.center.row = fig.center.row + 1;
	re.center.column = fig.center.column;
	re.p1.row = fig.p1.row + 1;
	re.p1.column = fig.p1.column;
	re.p2.row = fig.p2.row + 1;
	re.p2.column = fig.p2.column;
	re.p3.row = fig.p3.row + 1;
	re.p3.column = fig.p3.column;
	re.p4.row = fig.p4.row + 1;
	re.p4.column = fig.p4.column;
	return re;
}

/*
 * Bewegt den Tetrisstein um 1 Feld nach rechts
 * Es sollte vorher getestet werden, ob die Bewegung möglich ist
 */
tetrisFigure moveRight(tetrisFigure fig) {
	tetrisFigure re;
	re.center.row = fig.center.row;
	re.center.column = fig.center.column + 1;
	re.p1.row = fig.p1.row;
	re.p1.column = fig.p1.column + 1;
	re.p2.row = fig.p2.row;
	re.p2.column = fig.p2.column + 1;
	re.p3.row = fig.p3.row;
	re.p3.column = fig.p3.column + 1;
	re.p4.row = fig.p4.row;
	re.p4.column = fig.p4.column + 1;
	return re;
}

/*
 * Bewegt den Tetrisstein um 1 Feld nach links
 * Es sollte vorher getestet werden, ob die Bewegung möglich ist
 */
tetrisFigure moveLeft(tetrisFigure fig) {
	tetrisFigure re;
	re.center.row = fig.center.row;
	re.center.column = fig.center.column - 1;
	re.p1.row = fig.p1.row;
	re.p1.column = fig.p1.column - 1;
	re.p2.row = fig.p2.row;
	re.p2.column = fig.p2.column - 1;
	re.p3.row = fig.p3.row;
	re.p3.column = fig.p3.column - 1;
	re.p4.row = fig.p4.row;
	re.p4.column = fig.p4.column - 1;
	return re;
}

/* Gibt zurück, ob sich die Figur nach unten bewegen kann
 *
 * 0 --> false / keine Bewegung nach unten möglich
 * anderer Wert --> true / Bewegung nach unten möglich
 */
char canMoveDown(tetrisFigure fig) {
	if (isPointSet(fig.p1.row + 1, fig.p1.column) == 0
			&& isPointSet(fig.p2.row + 1, fig.p2.column) == 0
			&& isPointSet(fig.p3.row + 1, fig.p3.column) == 0
			&& isPointSet(fig.p4.row + 1, fig.p4.column) == 0) {
		return 1;
	} else {
		return 0;
	}
}

/*
 * Prüft, ob die Figur gedreht werden kann
 */
char canRotate(tetrisFigure fig) {
	if (isPointSet(rotatePoint(fig.p1, fig.center)) == 0
			&& isPointSet(rotatePoint(fig.p2, fig.center)) == 0
			&& isPointSet(rotatePoint(fig.p3, fig.center)) == 0
			&& isPointSet(rotatePoint(fig.p4, fig.center)) == 0) {
		return 1;
	} else {
		return 0;
	}
}

/*
 * Gibt zurück, ob sich die Figur nach rechts bewegen kann
 *
 * 0 --> false / keine Bewegung nach rechts möglich
 * anderer Wert --> true / Bewegung nach rechts möglich
 */
char canMoveRight(tetrisFigure fig) {
	if (isPointSet(fig.p1.row, fig.p1.column + 1) == 0
			&& isPointSet(fig.p2.row, fig.p2.column + 1) == 0
			&& isPointSet(fig.p3.row, fig.p3.column + 1) == 0
			&& isPointSet(fig.p4.row, fig.p4.column + 1) == 0) {
		return 1;
	} else {
		return 0;
	}
}

/*
 * Gibt zurück, ob sich die Figur nach links bewegen kann
 *
 * 0 --> false / keine Bewegung nach links möglich
 * anderer Wert --> true / Bewegung nach links möglich
 */
char canMoveLeft(tetrisFigure fig) {
	if (isPointSet(fig.p1.row, fig.p1.column - 1) == 0
			&& isPointSet(fig.p2.row, fig.p2.column - 1) == 0
			&& isPointSet(fig.p3.row, fig.p3.column - 1) == 0
			&& isPointSet(fig.p4.row, fig.p4.column - 1) == 0) {
		return 1;
	} else {
		return 0;
	}
}

/*
 * Markiert einen Punkt der Matrix als belegt
 */
void setPoint(unsigned char row, unsigned char column) {
	statePoints[row] |= twoPot(column);
}

/*
 * Löscht alle vollen Linien aus der Matrix und verschiebt den Rest nach unten
 * Gibt die Anzahl der gelöschten Linien zurück
 */
char cleanFullLanes() {
	char score = 0;
	char row = ROWS - 1;
	while (row >= 0) {
		if (statePoints[row] == 255) {
			for (char x = row; x > 0; --x) {
				statePoints[x] = statePoints[x - 1];
			}
			score = score + 1;
			printf("Line %d cleaned", row);
		} else {
			row = row - 1;
		}
	}
	return score;
}

/*
 * Funktion, in der der Tetris-Algorithmus untergebracht werden soll
 */
void tetris() {
	tetrisFigure current, next;
	current = createRandomFigure();
	next = createRandomFigure();
	int score = 0;
	while (canMoveDown(next) != 0) {
		for (int var2 = 0; var2 < 100000000; ++var2) {
			if (var2 % 1000000 == 0) {
				if (kbhit()) {
					char c = getch();
					if ((c == 'd' || c == 'D') && canMoveRight(current) != 0) {
						current = moveRight(current);
						setAllLED(statePoints);
						onLED(current.p1.row, current.p1.column);
						onLED(current.p2.row, current.p2.column);
						onLED(current.p3.row, current.p3.column);
						onLED(current.p4.row, current.p4.column);
						showDisplay();
					}
					if ((c == 'a' || c == 'A') && canMoveLeft(current) != 0) {
						current = moveLeft(current);
						setAllLED(statePoints);
						onLED(current.p1.row, current.p1.column);
						onLED(current.p2.row, current.p2.column);
						onLED(current.p3.row, current.p3.column);
						onLED(current.p4.row, current.p4.column);
						showDisplay();
					}
					if (c == ' ' && canRotate(current) != 0) {
						current = rotate(current);
						setAllLED(statePoints);
						onLED(current.p1.row, current.p1.column);
						onLED(current.p2.row, current.p2.column);
						onLED(current.p3.row, current.p3.column);
						onLED(current.p4.row, current.p4.column);
						showDisplay();
					}
				}
			}

		}
		if (canMoveDown(current) != 0) {
			current = moveDown(current);
		} else {
			setPoint(current.p1.row, current.p1.column);
			setPoint(current.p2.row, current.p2.column);
			setPoint(current.p3.row, current.p3.column);
			setPoint(current.p4.row, current.p4.column);
			score += cleanFullLanes();
			current = next;
			next = createRandomFigure();
		}
		setAllLED(statePoints);
		onLED(current.p1.row, current.p1.column);
		onLED(current.p2.row, current.p2.column);
		onLED(current.p3.row, current.p3.column);
		onLED(current.p4.row, current.p4.column);
		showDisplay();
	}
	printf("\nYour score is: %d", score);
	for (int var2 = 0; var2 < 1000000000; ++var2) {
	}
}
